package models

import (
	// "errors"
	// "fmt"
	//	"log"
	// "reflect"
	// "strings"
	"time"

	// "github.com/astaxie/beego"
	"gitee.com/lzqysoft/ims/libs"
	"gitee.com/lzqysoft/ims/libs/strtool"
	"github.com/astaxie/beego"
	"github.com/astaxie/beego/orm"
)

type TransitionInstance struct {
	Id           string        `orm:"column(id);pk"`
	EventId      string        `orm:"column(event_id)"`
	Action       *Action       `orm:"column(action_id);rel(fk)"`
	Flow         *Flow         `orm:"column(flow_id);rel(fk)"`
	Flowinstance *Flowinstance `orm:"column(flowinstance_id);rel(fk)"`
	Transition   *Transition   `orm:"column(transition_id);rel(fk)"`
	Name         string        `orm:"column(name);null"`
	From         string        `orm:"column(from);null"`
	To           string        `orm:"column(to);null"`
	Fromid       string        `orm:"column(fromid);null"`
	Toid         string        `orm:"column(toid);null"`
	Remark       string        `orm:"column(remark);null"`
	Status       int           `orm:"column(status)"`
	Isdelete     int           `orm:"column(isdelete)"`
	Ctime        time.Time     `orm:"column(ctime);type(timestamp with time zone)"`
	Creater      string        `orm:"column(creater)"`
	Utime        time.Time     `orm:"column(utime);type(timestamp with time zone)"`
	Updater      string        `orm:"column(updater)"`
	UserId       string        `orm:"column(user_id)"`
	Result       int           `orm:"-"`
	Opinions     string        `orm:"-"`
}

func (t *TransitionInstance) TableName() string {
	return "fsm_transitioninstance"
}

func init() {
	orm.RegisterModel(new(TransitionInstance))
}

func NewTransitionInstance() *TransitionInstance {
	return &TransitionInstance{
		Id:       libs.UUID(),
		Status:   1,
		Isdelete: 0,
		Ctime:    time.Now(),
		Utime:    time.Now(),
	}
}

// AddTransitionInstance insert a new TransitionInstance into database and returns
// last inserted Id on success.
func AddTransitionInstance(m *TransitionInstance) (id int64, err error) {
	o := orm.NewOrm()
	id, err = o.Insert(m)
	return
}

// GetTransitionInstanceById retrieves TransitionInstance by Id. Returns error if
// Id doesn't exist
func GetTransitionInstanceById(id string) (v *TransitionInstance, err error) {
	o := orm.NewOrm()
	v = &TransitionInstance{Id: id}
	if err = o.Read(v); err == nil {
		return v, nil
	}
	return nil, err
}

// GetTransitionInstanceByFlow 根据流程实例获取转换规则
func GetTransitionInstanceByFlow(flowid string) (ml []map[string]interface{}, err error) {
	o := orm.NewOrm()
	qs := o.QueryTable(new(TransitionInstance))
	qs = qs.Filter("Isdelete", 0).Filter("Status", 1).Filter("Flowinstance", flowid).RelatedSel()
	var l []TransitionInstance
	_, err = qs.Limit(-1).All(&l)
	// beego.Debug("Flowinstance", flowid)
	// beego.Debug("l", l)
	for _, v := range l {
		u := make(map[string]interface{})
		from, _ := GetStateinstanceById(v.From)
		// beego.Debug(err)
		to, _ := GetStateinstanceById(v.To)
		// beego.Error(err, v.To)
		ev, _ := GetEventById(v.EventId)
		// beego.Error(err)
		u["From"] = v.From
		u["To"] = v.To
		u["Action"] = v.Action.Id
		u["Event"] = v.EventId
		u["FromName"] = from.Text
		u["ToName"] = to.Text
		u["ActionName"] = v.Action.Name
		u["EventName"] = ev.Name
		u["Id"] = v.Id
		ml = append(ml, u)
	}

	return
}

// GetFromByFork 根据流程实例获取转换规则
func GetFromByFork(forkid string) (ml []*TransitionInstance, err error) {
	o := orm.NewOrm()
	qs := o.QueryTable(new(TransitionInstance))
	qs = qs.Filter("To", forkid)
	_, err = qs.Limit(-1).All(&ml)
	return
}

// autoGenerateTransitionInstance 自动生成下一步状态转换实例
func autoGenerateTransitionInstance(o orm.Ormer, flow *Flowinstance, formStateInstance *Stateinstance, user *User) error {
	//o := orm.NewOrm()
	//o.Begin()
	// 查询 transition
	var list []Transition
	if _, err := o.QueryTable(new(Transition)).Filter("Isdelete", 0).Filter("From", formStateInstance.State.Id).
		All(&list); err != nil {
		beego.Debug(err)
		//o.Rollback()
		return err
	}
	for _, t := range list {
		ts := NewTransitionInstance()
		ts.Action = t.Action
		ts.Creater = user.Name
		ts.EventId = t.EventId
		ts.Flow = t.Flow
		ts.Flowinstance = flow

		//from, err := GetStateinstanceByFlowInsAndState(flow.Id, t.From)
		//if err != nil {
		//	beego.Error(err)
		//	o.Rollback()
		//	return  err
		//}
		var from Stateinstance
		qs := o.QueryTable(new(Stateinstance))
		qs = qs.Filter("Flowinstance", flow.Id).Filter("State", t.From)
		qs = qs.RelatedSel()
		if err := qs.One(&from); err != nil {
			beego.Error(err)
			//o.Rollback()
			return err
		}
		var to Stateinstance
		qs = o.QueryTable(new(Stateinstance))
		qs = qs.Filter("Flowinstance", flow.Id).Filter("State", t.To)
		qs = qs.RelatedSel()
		if err := qs.One(&to); err != nil {
			beego.Error(err)
			//o.Rollback()
			return err
		}
		//to, err := GetStateinstanceByFlowInsAndState(flow.Id, t.To)
		//if err != nil {
		//	beego.Error(err)
		//	o.Rollback()
		//	return  err
		//}
		ts.From = from.Id
		ts.To = to.Id
		fstr := strtool.GetBetweenStr(t.Fromid, "$")
		ts.Fromid = ts.From + fstr
		tstr := strtool.GetBetweenStr(t.Toid, "$")
		ts.Toid = ts.To + tstr
		ts.Name = t.Name
		ts.Transition = &t
		ts.Updater = user.Name
		ts.Utime = ts.Ctime
		_, err := o.Insert(ts)
		if err != nil {
			beego.Error(err)
			//o.Rollback()
			return err
		}
	}
	return nil
}

func GetTransitionByCurrentStateInstance(fromId string, userId string) ([]TransitionInstance, error) {
	o := orm.NewOrm()
	var l []TransitionInstance
	_, err := o.QueryTable(new(TransitionInstance)).Filter("Isdelete", 0).Filter("From", fromId).
		Filter("UserId", userId).All(&l)
	if err != nil {
		return nil, err
	}
	return l, err
}

func GetStateInsByFlowAndFlag(flowInsId, flag string) ([]Stateinstance, error) {
	o := orm.NewOrm()
	var l []Stateinstance
	_, err := o.QueryTable(new(Stateinstance)).Filter("flowinstance_id", flowInsId).Filter("opinions", flag).
		Filter("isdelete", 0).All(&l)
	if err != nil {
		return nil, err
	}
	return l, err
}

func GetTransitionByUserId(flowid string, userId string) ([]TransitionInstance, error) {
	o := orm.NewOrm()
	var l []TransitionInstance
	_, err := o.QueryTable(new(TransitionInstance)).Filter("Isdelete", 0).
		Filter("UserId", userId).Filter("flowinstance_id", flowid).Filter("Status", 1).RelatedSel().All(&l)
	if err != nil {
		return nil, err
	}
	return l, err
}

// autoGenerateTransitionInstance 自动生成下一步状态转换实例
func autoGenerateTransitionInstanceUseUserId(o orm.Ormer, flow *Flowinstance, from, to *Stateinstance,
	user *User) error {
	//o := orm.NewOrm()
	//o.Begin()
	// 查询 transition
	var list []Transition
	if _, err := o.QueryTable(new(Transition)).Filter("Isdelete", 0).Filter("From", from.State.Id).
		Filter("To", to.State.Id).
		All(&list); err != nil {
		beego.Debug(err)
		//o.Rollback()
		return err
	}
	for _, t := range list {
		ts := NewTransitionInstance()
		ts.Action = t.Action
		ts.Creater = user.Name
		ts.EventId = t.EventId
		ts.Flow = t.Flow
		ts.Flowinstance = flow

		//from, err := GetStateinstanceByFlowInsAndState(flow.Id, t.From)
		//if err != nil {
		//	beego.Error(err)
		//	o.Rollback()
		//	return  err
		//}
		//var from Stateinstance
		//qs := o.QueryTable(new(Stateinstance))
		//qs = qs.Filter("Flowinstance", flow.Id).Filter("State", t.From)
		//qs = qs.RelatedSel()
		//if err := qs.One(&from); err != nil {
		//	beego.Error(err)
		//	//o.Rollback()
		//	return err
		//}
		//var to Stateinstance
		//qs = o.QueryTable(new(Stateinstance))
		//qs = qs.Filter("Flowinstance", flow.Id).Filter("State", t.To)
		//qs = qs.RelatedSel()
		//if err := qs.One(&to); err != nil {
		//	beego.Error(err)
		//	//o.Rollback()
		//	return err
		//}
		//to, err := GetStateinstanceByFlowInsAndState(flow.Id, t.To)
		//if err != nil {
		//	beego.Error(err)
		//	o.Rollback()
		//	return  err
		//}
		ts.From = from.Id
		ts.To = to.Id
		fstr := strtool.GetBetweenStr(t.Fromid, "$")
		ts.Fromid = ts.From + fstr
		tstr := strtool.GetBetweenStr(t.Toid, "$")
		ts.Toid = ts.To + tstr
		ts.Name = t.Name
		ts.Transition = &t
		ts.Updater = user.Name
		ts.Utime = ts.Ctime
		ts.UserId = from.UserId
		_, err := o.Insert(ts)
		if err != nil {
			beego.Error(err)
			//o.Rollback()
			return err
		}
	}
	return nil
}

func UpdateTransitionByIdAndTo(o orm.Ormer, id, tid string) error {
	sql1 := `UPDATE fsm_transitioninstance SET status = 2 WHERE "id" = ?`
	sql2 := `UPDATE fsm_transitioninstance SET status = 3 WHERE "id" IN (SELECT "id" FROM fsm_transitioninstance WHERE "from"=? AND "id" != ?)`
	if _, err := o.Raw(sql1, id).Exec(); err != nil {
		return err
	}
	if _, err := o.Raw(sql2, tid, id).Exec(); err != nil {
		return err
	}
	return nil
}
